package cs348a.test.pilot;

import java.util.ArrayList;
import java.util.List;

import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

import cs348a.data.CurveEvaluator;
import cs348a.data.SequenceData;
import cs348a.data.Site;
import cs348a.opengl.JoglPerspective;

public class PointPilot extends AbstractPilot {
	private JoglPerspective perspective;
	
	private SequenceData seqData;
	private CurveEvaluator ce = new CurveEvaluator();
	
	/** The number of segments per curve */
	private int numSeg = 10;
	
	/** List of points */
	private ArrayList<float[]> pointList = new ArrayList<float[]>();
	
	/** The current position */
	private int currPos = 0;
	
	private PilotThread pt;
		
	/**
	 * Constructor
	 * 
	 * @param p
	 */
	public PointPilot(JoglPerspective p){
		perspective = p;
		perspective.angle = 150;
	}
	
	@Override
	public void setSequenceData(SequenceData sd) {
		seqData = sd;
		
		seqData.addChangeListener(new ChangeListener() {
			
			@Override
			public void stateChanged(ChangeEvent e) {
				createPointList();
			}
		});
		
		// create sequence of points to go to
		currPos = 0;
		createPointList();
	}
	
	/**
	 * Creates a list of points to go through
	 */
	private void createPointList() {
		pointList.clear();
//		currPos = 0;
		
		List<Site> siteList = seqData.siteList;
		
		int len = siteList.size();
		
		for(int i = 0; i < len-1; i++) {
			
			if(siteList.get(i).isOnCurve) {
				// TODO: safety check points
				Site s1 = siteList.get(i);
				Site s2 = siteList.get(i+1);
				Site s3 = siteList.get(i+2);
				
				ce.setControlPoints(s1, s2, s3);
				
				for(int j = 0; j < numSeg; j++) {
					float[] p = ce.evaluate(j/(float)numSeg);

					pointList.add(p);
				}
			}
		}
		
		// Add last point
		pointList.add(ce.evaluate(1));
	}
	
	@Override
	public int getCurveNo() {
		return currPos / numSeg;
	}
	
	@Override
	public int getSegmentNo(){
		return currPos % numSeg;
	}
	
	@Override
	public void setPosition(int curveNo, int segNo) {
		currPos = curveNo*numSeg + segNo;
		
		currentPoint = pointList.get(currPos).clone();
		goToPosition(currentPoint);
		fireChanged(currentPoint);
	}	
	
	@Override
	public void startTour() {
		// start thread
		if(pt == null || !pt.isRunning()) {
			pt = new PilotThread();
			pt.start();
		}
	}
	
	@Override
	public void stopTour() {
		pt.abort();
	}
	
	/**
	 * Resets the tour
	 */
	public void resetTour() {
		currPos = 0;
	}
	
	@Override
	public boolean isRunning() {
		if(pt == null) return false;
		else return pt.isRunning();
	}
	
	/**
	 * Set to position
	 * 
	 * @param p
	 */
	public void goToPosition(float[] p) {
		currentPoint[2] += zOffset;
		
		if(isLookingDown)
			lookDown();
		else 
			lookForward();
	}
	
	private void lookDown() {
		perspective.upDirection = new float[] {0, 1, 0};
		perspective.viewPosition = currentPoint;

		float[] p = currentPoint.clone(); 
		p[2] = 0;
		perspective.objectPosition = p;
	}

	private void lookForward() {
		float[] p1 = pointList.get(currPos+1).clone();
		p1[2] += zOffset;
		perspective.upDirection = new float[] {0, 0, 1};
		perspective.viewPosition = currentPoint;
		perspective.objectPosition = p1;
	}
	
	public class PilotThread extends Thread {
		private boolean runFlag = true;
		
		public void run(){
			
			while(runFlag){
				try {
					currentPoint = pointList.get(currPos).clone();
					goToPosition(currentPoint);
					fireChanged(currentPoint);

					// increment counters
					currPos++;
					
					if(currPos == pointList.size()-1) {
						// handle final point
						sleep(timeInterval);
						currentPoint = pointList.get(currPos).clone();
						currentPoint[2] += zOffset;
						lookDown();
						fireChanged(currentPoint);
						
						currPos = 0;
						runFlag = false;
						return;
					} else {
						sleep(timeInterval);
					}
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}

		public void abort(){
			runFlag = false;
		}
		
		public boolean isRunning() {
			return runFlag;
		}
	}
}
